package io.elegant.sessions

import io.grpc.stub.StreamObserver
import com.google.protobuf.Any
import io.elegant.Elegant.Interaction
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.cancel
import kotlinx.coroutines.launch
import java.lang.Exception

abstract class AbstractSession {
    protected val csMain = CoroutineScope(Dispatchers.Main)
    protected val csIo = CoroutineScope(Dispatchers.IO)
    protected abstract val sendStreamObserver: StreamObserver<Interaction>
    protected val receiveStreamObserver = object: StreamObserver<Interaction> {
        override fun onCompleted() {
            println("Stream done.")
            csMain.launch {
                onDestroy(null)
            }
        }
        override fun onError(t: Throwable?) {
            t?.printStackTrace()
            csMain.launch {
                onDestroy(t)
            }
        }
        override fun onNext(value: Interaction) {
            csMain.launch {
                try {
                    onReceive(value.name, value.session, value.future, *value.argvList.toTypedArray())
                } catch (e: Exception) {
                    onError(e, value.session, value.future)
                    e.printStackTrace()
                }
            }
        }
    }
    abstract fun create(sessionId: Long = 0, futureId: Long = 0, vararg argv: Any)
    open fun destroy() = try {
        sendStreamObserver.onCompleted()
    } catch (e: IllegalStateException) {
        e.printStackTrace()
    } finally {
        try {
            csIo.cancel(null)
        } catch (e: IllegalStateException) {
            e.printStackTrace()
        }
        try {
            csMain.cancel(null)
        } catch (e: IllegalStateException) {
            e.printStackTrace()
        }
    }
    fun send(request: Interaction) = try {
        sendStreamObserver.onNext(request)
    } catch (e: IllegalArgumentException) {
        e.printStackTrace()
    } catch (e: IllegalStateException) {
        e.printStackTrace()
    }
    abstract suspend fun onReceive(name: String, sessionId: Long = 0, futureId: Long = 0, vararg argv: Any)
    abstract suspend fun onDestroy(throwable: Throwable?)
    abstract suspend fun onError(throwable: Throwable?, sessionId: Long = 0, futureId: Long = 0)
}